package org.jugile.util;

/*

Copyright (C) 2007-2011 Jukka Rahkonen  email: jukka.rahkonen@iki.fi

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * "And he had in his right hand seven stars: and out of his mouth went a sharp 
 *  twoedged sword: and his countenance was as the sun shineth in his strength.
 *  And when I saw him, I fell at his feet as dead. And he laid his right hand upon me, 
 *  saying unto me, Fear not; I am the first and the last.
 *  I am he that liveth, and was dead; and, behold, I am alive for evermore, 
 *  Amen; and have the keys of hell and of death." (Rev 1:16-18)
 * 
 * ==========
 * 
 * @author jukka.rahkonen@iki.fi
 */
public class Duration extends Jugile implements Serializable {
	private static final long serialVersionUID = 1L;
	
	private final long millis;
	
	public Duration(long ms) {
		millis = ms;
	}
	
	public Duration(int days, int hours, int minutes, int seconds, int ms) {
		millis = getMsec(days, hours, minutes, seconds, ms);
	}
	public Duration(int days, int hours, int minutes, int seconds) {
		millis = getMsec(days, hours, minutes, seconds);
	}
	public Duration(int hours, int minutes, int seconds) {
		millis = getMsec(0, hours, minutes, seconds);
	}
	public Duration(int hours, int minutes) {
		millis = getMsec(0, hours, minutes, 0);
	}
	public Duration(int hours) {
		millis = getMsec(0, hours, 0, 0);
	}

	public long getMs() { return millis; }
	
	public String toString() {
		long ms = millis;
		int hours = getHours();
		ms = ms - hours*1000*60*60;
		int mins = (int)(ms/1000/60);
		ms = ms - mins*1000*60;
		int secs = (int)(ms/1000);
		ms = ms - secs*1000;
		int mils = (int)ms;
		if ((millis % 1000) != 0) return ""+hours+":"+zpad(mins)+":"+zpad(secs)+":"+zpad3(mils);
		if ((millis % (1000*60)) != 0) return ""+hours+":"+zpad(mins)+":"+zpad(secs);
		return ""+hours+":"+zpad(mins);
	}
	
	private String zpad(int n) {
		if (n < 10) return "0"+n;
		return ""+n;
	}
	private String zpad3(int n) {
		if (n < 10) return "00"+n;
		if (n < 100) return "0"+n;
		return ""+n;
	}
	
	public int getHours() {
		return (int)(millis/1000/60/60);
	}
	
	public int getHoursCeil() {
		return (int)Math.ceil(millis/1000.0/60.0/60.0);
	}
	
	public boolean isZero() {
		return millis == 0;
	}
	
	
	public Duration plus(Duration d) {
		if (d == null) return this;
		return new Duration(getMs() + d.getMs());
	}
	public Duration minus(Duration d) {
		if (d == null) return this;
		return new Duration(getMs() - d.getMs());
	}
	public Duration mult(int f) {
		return new Duration(getMs() * (long)f);
	}
	public Duration div(int d) {
		if (d == 0) U.fail("divide by zero");
		return new Duration(getMs() / d);
	}
	
	public int getSeconds() {
		return (int)(millis/1000);
	}
	
	private static Pattern p0 = Pattern.compile("(\\d+) (\\d+):(\\d\\d):(\\d\\d)");
	private static Pattern p1 = Pattern.compile("(\\d+):(\\d\\d):(\\d\\d)");
	private static Pattern p2 = Pattern.compile("(\\d+):(\\d\\d)");
	private static Pattern p3 = Pattern.compile("(\\d+)");
	private static Pattern p4 = Pattern.compile("(\\d+)d");

	private static Pattern ms1 = Pattern.compile("\\d");
	private static Pattern ms2 = Pattern.compile("\\d\\d");
	private static Pattern ms3 = Pattern.compile("\\d\\d\\d");
	
	private int getMs(String str) {
		Matcher m = ms3.matcher(str);
		if (m.matches()) {
			return Integer.parseInt(str);
		}
		m = ms2.matcher(str);
		if (m.matches()) {
			return 10 * Integer.parseInt(str);
		}
		m = ms1.matcher(str);
		if (m.matches()) {
			return 100 * Integer.parseInt(str);
		}
		U.fail("invalid decimals: " + str);
		return 0;
	}
	
	public Duration(String str) {
		Matcher m = null;

		// check for "."
		String parts[] = str.split("\\.");
		String str1 = parts[0];
		
		int ms = 0;
		if (parts.length > 1) ms = getMs(parts[1]);
			
		m = p0.matcher(str1);
		if (m.matches()) {
			int days = Integer.parseInt(m.group(1));
			int hours = Integer.parseInt(m.group(2));
			int mins = Integer.parseInt(m.group(3));
			int secs = Integer.parseInt(m.group(4));
			millis = getMsec(days, hours, mins, secs, ms);
			return;
		}

		m = p1.matcher(str1);
		if (m.matches()) {
			int hours = Integer.parseInt(m.group(1));
			int mins = Integer.parseInt(m.group(2));
			int secs = Integer.parseInt(m.group(3));
			millis = getMsec(0, hours, mins, secs, ms);
			return;
		}
		
		m = p2.matcher(str1);
		if (m.matches()) {
			int hours = Integer.parseInt(m.group(1));
			int mins = Integer.parseInt(m.group(2));
			if (ms > 0) millis = getMsec(0, 0, hours, mins, ms);
			else millis = getMsec(0, hours, mins, 0, 0);
			return;
		}

		m = p3.matcher(str1);
		if (m.matches()) {
			int hours = Integer.parseInt(m.group(1));
			if (ms > 0) millis = getMsec(0, 0, 0, hours, ms);
			else millis = getMsec(0, hours, 0, 0, 0);
			return;
		}
		
		m = p4.matcher(str1);
		if (m.matches()) {
			int days = Integer.parseInt(m.group(1));
			millis = getMsec(days, 0, 0, 0, ms);
			return;
		}

		millis = 0L;
		U.fail("invalid duration format: " + str);
	}

	private long getMsec(int days, int hours, int minutes, int seconds) {
		return 1000L * (seconds + minutes * 60 + hours * 60 * 60 + days * 60 * 60 * 24);
	}

	private long getMsec(int days, int hours, int minutes, int seconds, int ms) {
		return 1000L * (seconds + minutes * 60 + hours * 60 * 60 + days * 60 * 60 * 24) + ms;
	}


}
